package com.example.forum.commons.http;


import com.example.forum.commons.annotations.UncheckToken;
import com.example.forum.commons.authrization.TokenManager;
import com.example.forum.commons.exceptions.ExceptionCode;
import com.example.forum.commons.exceptions.ServiceException;
import com.example.forum.commons.jwt.JwtHelper;
import com.example.forum.commons.tools.Constants;
import com.example.forum.commons.tools.JsonUtil;
import com.example.forum.commons.tools.NetworkUtil;
import com.example.forum.commons.tools.ThreadLocalUtil;
import com.example.forum.commons.tools.redis.RedisHelper;
import com.example.forum.service.ITIpallowedService;
import com.example.forum.vo.CurrentUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
import java.util.UUID;

@Configuration
public class GlobalRequestHandler implements WebMvcConfigurer {
    @Value("${rememberMe.time}")
    private String rememberMeTime;

    @Autowired
    private RedisHelper redisHelper;

    @Autowired
    private ITIpallowedService ipAllowedService;

    @Autowired
    private TokenManager tokenManager;
    @Value("${ifIpCheck}")
    private boolean IfIPCheck;

    @Autowired
    private JwtHelper jwtHelper;

    private static final Logger log = LoggerFactory.getLogger(GlobalRequestHandler.class);

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(requestInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/swagger-resources/**")
                .excludePathPatterns("/User/login")
                .excludePathPatterns("/AppUser/login")
                .excludePathPatterns("/webjars/**")
                .excludePathPatterns("/webjars/springfox-swagger-ui/css/typography.css")
                .excludePathPatterns("/v2/**")
                .excludePathPatterns("/error")
                .excludePathPatterns("/swagger-ui.html/**");
        //  super.addInterceptors(registry);


    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");

        registry.addResourceHandler("/swagger-resources/**")
                .addResourceLocations("classpath:/META-INF/resources/swagger-resources/");

        registry.addResourceHandler("/swagger/**")
                .addResourceLocations("classpath:/META-INF/resources/swagger*");

        registry.addResourceHandler("/v2/api-docs/**")
                .addResourceLocations("classpath:/META-INF/resources/v2/api-docs/");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//设置允许跨域的路径
                .allowedOrigins("*")//设置允许跨域请求的域名
                .allowCredentials(true)//是否允许证书 不再默认开启
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")//设置允许的方法
                .maxAge(3600);
    }


    public HandlerInterceptorAdapter requestInterceptor() {
        return new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                interceptorHandler(request, handler);
                return true;
            }
        };
    }

    private void interceptorHandler(HttpServletRequest request, Object handler) throws Exception {
        //校验IP
            checkIP(request);
        //校验参数
        // checkParam(request);
        //校验token（token放在cookie和redis里，这是针对H5，如果是app可以放到response头里，用request头接收）
        // checkTokenAndRole(request, handler);
        jwtAuthorization(request, handler);
        //语言国际化
        locale(request);
        //防重放
        //   antiReplay();
    }

    private void checkIP(HttpServletRequest request) throws IOException {
        if (!IfIPCheck) {
            return;
        }
        String ip = NetworkUtil.getIpAddress(request);
        var boolAllowed = ipAllowedService.IfAllowedIP(ip);
        if (!boolAllowed) {
            throw new ServiceException(HttpStatus.NOT_ACCEPTABLE.value(), "Ip 不被允许访问");
        }

    }

    private void checkParam(HttpServletRequest request) {
        String appName = request.getHeader(Constants.APP_NAME);
        String appVersion = request.getHeader(Constants.APP_VERSION);
        if (StringUtils.isEmpty(appName) || StringUtils.isEmpty(appVersion)) {
            throw new ServiceException(ExceptionCode.HANDLER_PARAM_ERROR.getCode(), "缺少必要参数handler param is null!");
        }

        //设置请求信息
        MDC.put(Constants.APP_NAME, appName);
        MDC.put(Constants.APP_VERSION, appVersion);
        MDC.put("requestId", UUID.randomUUID().toString());
    }

    private void checkTokenAndRole(HttpServletRequest request, Object handle) {
        if (((HandlerMethod) handle).getMethod().isAnnotationPresent(UncheckToken.class)) {
            return;
        }
        String token = request.getHeader(Constants.TOKEN);
        if (StringUtils.isEmpty(token)) {
            throw new ServiceException(ExceptionCode.NEED_LOGIN.getCode(), "token缺失");
        }
        //此处是用token来找用户信息，不是极端情况不会出现重复问题，但是达到数十万级的QPS时候，UUID还是会出现重复现象，可以token+用户名形式做key
        if (tokenManager.GetUserIdAsync(token) == null) {
            String message = String.format("token [%s] is invalid", token);
            log.debug("message : " + message);
            throw new ServiceException(ExceptionCode.NEED_LOGIN.getCode(), "token过期");

        }
        String userInfo = redisHelper.readValue(token);
        if (StringUtils.isEmpty(userInfo)) {
            throw new ServiceException(ExceptionCode.NEED_LOGIN.getCode(), "token过期");
        } else {
            //刷新有效期
            tokenManager.UpdateTokenExpireDate(token);

            //缓存当前用户
            CurrentUser currentUser = JsonUtil.String2Object(userInfo, CurrentUser.class);
            ThreadLocalUtil.set(currentUser);

            //鉴权, 没写@RolesAllowed为任何权限均可访问
            String userRole = currentUser.getRole();
            RolesAllowed rolesAllowed = ((HandlerMethod) handle).getMethod().getAnnotation(RolesAllowed.class);
            if (rolesAllowed != null && rolesAllowed.value().length != 0) {
                if (!Arrays.asList(rolesAllowed.value()).contains(userRole)) {
                    throw new ServiceException(ExceptionCode.AUTH_ERROR.getCode(), "无权访问");
                }
            }
        }
    }



    //jwt用户身份认证
    private void jwtAuthorization(HttpServletRequest request, Object handle) {
        if (!request.getMethod().toString().equals("OPTIONS")) {
            //排除UncheckToken注解的接口
            if (((HandlerMethod) handle).getMethod().isAnnotationPresent(UncheckToken.class)) {
                return;
            }
            String token = request.getHeader(Constants.HEADER_STRING);
            if (StringUtils.isEmpty(token)) {
                throw new ServiceException(ExceptionCode.AUTH_ERROR.getCode(), "token缺失");
            }
            //检验token是否有效
            var flag = jwtHelper.validateTokenAndGetClaims(token);
            if (flag == false) {
                throw new ServiceException(ExceptionCode.AUTH_ERROR.getCode(), "未授权或授权已过期");
            }
        }
    }


    private void locale(HttpServletRequest request) {
        String lang = request.getHeader(Constants.LANG);
        LocaleContextHolder.setLocale(StringUtils.isEmpty(lang) ? Locale.SIMPLIFIED_CHINESE : Locale.forLanguageTag(lang));
    }

    private void antiReplay() {
        //timestamp + uuid
    }
}
